package org.example; // 定义包名，将PlaneGame类组织在org.example包中

import javax.swing.*; // 导入Swing GUI组件库，用于创建图形界面
import java.awt.*; // 导入AWT基础图形库，用于绘图和界面布局
import java.awt.event.*; // 导入AWT事件处理库，用于处理键盘和鼠标事件
import java.util.ArrayList; // 导入ArrayList集合类，用于存储动态数组
import java.util.Iterator; // 导入Iterator迭代器接口，用于遍历集合元素
import java.util.List; // 导入List接口，定义集合操作规范
import java.util.Random; // 导入Random随机数生成器，用于产生随机数

/**
 * 问题代码示例：混合了游戏逻辑和界面显示的飞机大战游戏
 * 展示了没有使用MVC架构时代码的混乱情况
 */
// 定义PlaneGame类，继承自JPanel，作为游戏主面板
public class PlaneGame extends JPanel {
    // 游戏状态变量
    private int score = 0; // 玩家得分，初始为0
    private boolean gameRunning = true; // 游戏运行状态标志，true表示游戏正在进行

    // 游戏对象
    private Player player; // 玩家对象引用
    private List<Bullet> bullets; // 存储所有子弹的列表
    private List<Enemy> enemies; // 存储所有敌机的列表

    // 游戏循环定时器
    private Timer gameTimer; // 游戏主循环定时器，控制游戏刷新频率
    private Random random; // 随机数生成器，用于生成随机事件

    // 构造函数，初始化游戏
    public PlaneGame() {
        // 初始化游戏对象
        player = new Player(200, 500); // 创建玩家对象，初始位置为(200, 500)
        bullets = new ArrayList<>(); // 初始化子弹列表为空列表
        enemies = new ArrayList<>(); // 初始化敌机列表为空列表
        random = new Random(); // 创建随机数生成器实例

        // 设置面板
        setPreferredSize(new Dimension(500, 600)); // 设置面板推荐尺寸为500x600像素
        setBackground(Color.BLACK); // 设置面板背景色为黑色
        setFocusable(true); // 设置面板可获得焦点，以便接收键盘事件

        // 设置输入监听
        setupInputHandling(); // 调用方法设置键盘输入监听器

        // 设置游戏循环
        setupGameLoop(); // 调用方法设置游戏主循环

        // 生成初始敌机
        spawnInitialEnemies(); // 调用方法生成初始敌机
    }

    /**
     * 输入处理直接放在界面类中
     */
    // 设置键盘输入处理逻辑的方法
    private void setupInputHandling() {
        // 添加键盘事件监听器
        addKeyListener(new KeyAdapter() {
            // 重写按键按下事件处理方法
            @Override
            public void keyPressed(KeyEvent e) {
                if (!gameRunning) return; // 如果游戏未运行则直接返回

                // 玩家移动逻辑直接在这里处理
                // 根据按下的键执行相应操作
                switch(e.getKeyCode()) {
                    case KeyEvent.VK_LEFT: // 按下左箭头键
                        player.move(-5); // 玩家向左移动5个单位
                        break;
                    case KeyEvent.VK_RIGHT: // 按下右箭头键
                        player.move(5); // 玩家向右移动5个单位
                        break;
                    case KeyEvent.VK_SPACE: // 按下空格键
                        // 发射子弹逻辑直接在这里
                        bullets.add(new Bullet(player.x + 22, player.y)); // 在玩家位置创建新子弹
                        break;
                    case KeyEvent.VK_R: // 按下R键
                        if (!gameRunning) { // 只有在游戏结束时才能重启
                            resetGame(); // 重置游戏
                        }
                        break;
                }
                repaint(); // 直接操作界面重绘，更新画面
            }
        });
    }

    /**
     * 游戏循环直接放在界面类中
     */
    // 设置游戏主循环的方法
    private void setupGameLoop() {
        // 创建定时器，每50毫秒执行一次游戏逻辑
        gameTimer = new Timer(50, new ActionListener() {
            // 重写定时器动作事件处理方法
            @Override
            public void actionPerformed(ActionEvent e) {
                if (!gameRunning) return; // 如果游戏未运行则直接返回

                // 更新游戏状态（混杂在界面中）
                updateGame(); // 更新游戏中各对象的状态

                // 检查碰撞（混杂在界面中）
                checkCollisions(); // 检查游戏中各对象间的碰撞

                // 生成新敌机（混杂在界面中）
                if (random.nextInt(100) < 8) { // 8%概率生成新敌机
                    spawnEnemy(); // 生成一个新敌机
                }

                repaint(); // 直接操作界面重绘
            }
        });
        gameTimer.start(); // 启动游戏定时器
    }

    /**
     * 更新游戏逻辑 - 应该放在模型中的代码
     */
    // 更新游戏状态的方法
    private void updateGame() {
        // 更新子弹位置
        Iterator<Bullet> bulletIter = bullets.iterator(); // 获取子弹列表的迭代器
        while (bulletIter.hasNext()) { // 遍历所有子弹
            Bullet bullet = bulletIter.next(); // 获取下一个子弹
            bullet.update(); // 更新子弹状态
            if (!bullet.active) { // 如果子弹已失效
                bulletIter.remove(); // 从列表中移除该子弹
            }
        }

        // 更新敌机位置
        Iterator<Enemy> enemyIter = enemies.iterator(); // 获取敌机列表的迭代器
        while (enemyIter.hasNext()) { // 遍历所有敌机
            Enemy enemy = enemyIter.next(); // 获取下一个敌机
            enemy.update(); // 更新敌机状态
            if (!enemy.active) { // 如果敌机已失效
                enemyIter.remove(); // 从列表中移除该敌机
            }
        }
    }

    /**
     * 碰撞检测 - 应该放在模型中的业务逻辑
     */
    // 检查碰撞的方法
    private void checkCollisions() {
        // 子弹与敌机碰撞
        Iterator<Bullet> bulletIter = bullets.iterator(); // 获取子弹列表的迭代器
        while (bulletIter.hasNext()) { // 遍历所有子弹
            Bullet bullet = bulletIter.next(); // 获取下一个子弹
            Iterator<Enemy> enemyIter = enemies.iterator(); // 获取敌机列表的迭代器
            while (enemyIter.hasNext()) { // 遍历所有敌机
                Enemy enemy = enemyIter.next(); // 获取下一个敌机
                // 检查子弹矩形区域是否与敌机矩形区域相交
                if (bullet.getBounds().intersects(enemy.getBounds())) {
                    // 业务逻辑直接在这里处理
                    bullet.active = false; // 标记子弹为失效状态
                    enemy.active = false; // 标记敌机为失效状态
                    score += 100; // 得分增加100分
                    break; // 跳出内层循环
                }
            }
        }

        // 玩家与敌机碰撞
        for (Enemy enemy : enemies) { // 遍历所有敌机
            // 检查玩家矩形区域是否与敌机矩形区域相交
            if (player.getBounds().intersects(enemy.getBounds())) {
                // 游戏结束逻辑直接在这里
                gameRunning = false; // 设置游戏状态为结束
                gameTimer.stop(); // 停止游戏定时器
                break; // 跳出循环
            }
        }
    }

    /**
     * 生成敌机 - 游戏逻辑混杂在界面中
     */
    // 生成一个新敌机的方法
    private void spawnEnemy() {
        int x = random.nextInt(400); // 在0-399之间随机生成敌机的X坐标
        enemies.add(new Enemy(x, -40)); // 在指定位置创建新敌机并添加到敌机列表
    }

    // 生成初始敌机的方法
    private void spawnInitialEnemies() {
        for (int i = 0; i < 3; i++) { // 循环3次
            spawnEnemy(); // 每次生成一个敌机
        }
    }

    // 重置游戏状态的方法
    private void resetGame() {
        score = 0; // 重置得分为0
        gameRunning = true; // 设置游戏状态为进行中
        bullets.clear(); // 清空子弹列表
        enemies.clear(); // 清空敌机列表
        player.x = 200; // 重置玩家X坐标
        player.y = 500; // 重置玩家Y坐标
        spawnInitialEnemies(); // 重新生成初始敌机
        gameTimer.start(); // 重新启动游戏定时器
    }

    /**
     * 绘制方法 - 包含大量游戏状态判断和显示逻辑
     */
    // 重写组件绘制方法
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); // 调用父类的绘制方法

        Graphics2D g2d = (Graphics2D) g; // 将Graphics对象转换为Graphics2D以获得更多绘图功能

        // 绘制玩家
        g2d.setColor(Color.GREEN); // 设置绘图颜色为蓝色
        g2d.fillRect(player.x, player.y, player.width, player.height); // 绘制玩家矩形

        // 绘制子弹
        g2d.setColor(Color.WHITE); // 设置绘图颜色为黄色
        for (Bullet bullet : bullets) { // 遍历所有子弹
            g2d.fillRect(bullet.x, bullet.y, bullet.width, bullet.height); // 绘制每个子弹
        }

        // 绘制敌机
        g2d.setColor(Color.ORANGE); // 设置绘图颜色为橙色
        for (Enemy enemy : enemies) { // 遍历所有敌机
            g2d.fillRect(enemy.x, enemy.y, enemy.width, enemy.height); // 绘制每个敌机
        }

        // 绘制分数和游戏状态（界面和业务逻辑混杂）
        g2d.setColor(Color.WHITE); // 设置绘图颜色为白色
        g2d.drawString("分数: " + score, 10, 20); // 在指定位置绘制当前得分

        if (!gameRunning) { // 如果游戏已结束
            g2d.setColor(Color.RED); // 设置绘图颜色为红色
            // 在绘制文字前设置支持中文的字体
            Font chineseFont = new Font("微软雅黑", Font.BOLD, 24); // 创建中文字体
            g2d.setFont(chineseFont); // 设置字体
            g2d.drawString("游戏结束! 按R重新开始", 100, 300); // 在指定位置绘制游戏结束提示
        }
    }

    // ==================== 内部类定义 ====================

    /**
     * 玩家类 - 作为内部类定义
     */
    // 玩家内部类
    class Player {
        int x, y; // 玩家的位置坐标
        int width = 50, height = 50; // 玩家的宽度和高度

        // 玩家构造函数
        public Player(int x, int y) {
            this.x = x; // 设置玩家初始X坐标
            this.y = y; // 设置玩家初始Y坐标
        }

        // 移动玩家的方法
        public void move(int deltaX) {
            x += deltaX; // 更新玩家X坐标
            // 边界检查（应该放在模型中的逻辑）
            if (x < 0) x = 0; // 防止玩家移出左边界
            if (x > 450) x = 450; // 防止玩家移出右边界
        }

        // 获取玩家边界矩形的方法
        public Rectangle getBounds() {
            return new Rectangle(x, y, width, height); // 返回玩家的矩形边界
        }
    }

    /**
     * 子弹类 - 作为内部类定义
     */
    // 子弹内部类
    class Bullet {
        int x, y; // 子弹的位置坐标
        int width = 10, height = 10; // 子弹的宽度和高度
        boolean active = true; // 子弹活动状态，true表示仍在屏幕上

        // 子弹构造函数
        public Bullet(int x, int y) {
            this.x = x; // 设置子弹初始X坐标
            this.y = y; // 设置子弹初始Y坐标
        }

        // 更新子弹状态的方法
        public void update() {
            y -= 8; // 向上移动8个单位
            if (y < 0) { // 如果子弹移出屏幕顶部
                active = false; // 标记子弹为失效状态
            }
        }

        // 获取子弹边界矩形的方法
        public Rectangle getBounds() {
            return new Rectangle(x, y, width, height); // 返回子弹的矩形边界
        }
    }

    /**
     * 敌机类 - 作为内部类定义
     */
    // 敌机内部类
    class Enemy {
        int x, y; // 敌机的位置坐标
        int width = 40, height = 40; // 敌机的宽度和高度
        boolean active = true; // 敌机活动状态，true表示仍在屏幕上

        // 敌机构造函数
        public Enemy(int x, int y) {
            this.x = x; // 设置敌机初始X坐标
            this.y = y; // 设置敌机初始Y坐标
        }

        // 更新敌机状态的方法
        public void update() {
            y += 3; // 向下移动3个单位
            if (y > 600) { // 如果敌机移出屏幕底部
                active = false; // 标记敌机为失效状态
            }
        }

        // 获取敌机边界矩形的方法
        public Rectangle getBounds() {
            return new Rectangle(x, y, width, height); // 返回敌机的矩形边界
        }
    }

    // ==================== 主方法 ====================

    // 程序入口点
    public static void main(String[] args) {
        // 使用SwingUtilities.invokeLater确保GUI创建在事件调度线程中执行
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("飞机大战 - 问题代码示例"); // 创建主窗口框架
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置关闭操作为退出程序

            PlaneGame game = new PlaneGame(); // 创建游戏面板实例
            frame.getContentPane().add(game); // 将游戏面板添加到窗口内容面板
            frame.pack(); // 调整窗口大小以适应其子组件
            frame.setLocationRelativeTo(null); // 将窗口居中显示
            frame.setVisible(true); // 显示窗口

            // 请求焦点以便接收键盘事件
            game.requestFocusInWindow(); // 让游戏面板获得焦点以响应键盘输入
        });
    }
}